#include "stdafx.h"
#include "DeferredPipeline.h"

#include "GlobalAppData.h"
#include "RenderingData.h"
#include "RenderingSettings.h"
#include "InputLayouts.h"
#include "LoadShader.h"
#include "RenderLowFreq.h"
#include "RenderParticlesForward.h"
#include "DebugDraw.h"

#include "Mesh.h"
#include "Material.h"
#include "LoadTexture.h"
#include "CSM.h"
#include "SSAO.h"
#include "Renderer.h"

using namespace mgfx;

DeferredPipeline::DeferredPipeline()
{
	mMSAASamples = mcore::GlobalAppData::Instance.MSAASamples;
	mMSAAEnabled = mMSAASamples > 1;

	initEffects();
	initLightGeometry();
	createDeferredRTs();

	//initLowFreq();
	//initRenderParticlesForward();

	mCSM = new CSM();
	mSSAO = new SSAO();
}

DeferredPipeline::~DeferredPipeline()
{
	delete mCSM;
	delete mSSAO;
}

DeferredPipeline & DeferredPipeline::get()
{
	static DeferredPipeline dp;
	return dp;
}

void DeferredPipeline::initEffects()
{
	ShaderDefine msaaDefine("MSAA", "1");

	//{
	//	std::stringstream ss;
	//	std::vector<ShaderDefine> defines;
	//	defines.push_back(ShaderDefine("SHADOWS", "1"));
	//	ss.str("");
	//	ss << CSM::CASCADE_MAP_SIZE;
	//	defines.push_back(ShaderDefine("CASCADE_MAP_SIZE", ss.str()));
	//	if(mMSAAEnabled) defines.push_back(msaaDefine);
	//	mShaderDirLight = loadShaderFromFile("DirLight.fx", defines);
	//}
	mShaderDirLight = mMSAAEnabled ? loadShaderFromFile("DirLight.fx", msaaDefine) : loadShaderFromFile("DirLight.fx");
	mShaderPointLight = mMSAAEnabled ? loadShaderFromFile("PointLight.fx", msaaDefine) : loadShaderFromFile("PointLight.fx");
	mShaderSpotLight = mMSAAEnabled ? loadShaderFromFile("SpotLight.fx", msaaDefine) : loadShaderFromFile("SpotLight.fx");

	{
		std::vector<ShaderDefine> defines;
		defines.push_back(ShaderDefine("SHADOWS", "1"));
		if(mMSAAEnabled) defines.push_back(msaaDefine);
		mShaderShadowedSpotLight = loadShaderFromFile("SpotLight.fx", defines);
	}

	mShaderCSM = loadShaderFromFile("CSM.fx");
	mShaderWireframe = loadShaderFromFile("Wireframe.fx");
	mShaderDrawGBuffer = mMSAAEnabled ? loadShaderFromFile("DrawGBuffer.fx", msaaDefine) : loadShaderFromFile("DrawGBuffer.fx");
	mShaderGeometryStage = loadShaderFromFile("GeometryStage.fx");
	mShaderShadowMap = loadShaderFromFile("ShadowMap.fx");
	mShaderDeferredFinal = loadShaderFromFile("DeferredFinal.fx");
}

void DeferredPipeline::initLightGeometry()
{
	MeshDesc meshDesc;
	meshDesc.PositionsOnly = true;
	meshDesc.FileName = "lightgeometry_sphere.3ds";
	mSphereMesh = Mesh::createFromFile(meshDesc);

	meshDesc.FileName = "lightgeometry_cone.3ds";
	mConeMesh = Mesh::createFromFile(meshDesc);
}

void DeferredPipeline::createDeferredRTs()
{	
	int scrWid = mcore::GlobalAppData::Instance.ScreenWidth;
	int scrHei = mcore::GlobalAppData::Instance.ScreenHeight;

	Renderer * rdr = Renderer::Instance;

	// depth rendertarget
	mRtDepth = rdr->addRenderTarget(scrWid, scrHei, DXGI_FORMAT_R32_FLOAT, mMSAASamples);
	// normal rendertarget
	mRtNormal = rdr->addRenderTarget(scrWid, scrHei, DXGI_FORMAT_R16G16B16A16_SNORM, mMSAASamples);
	// diffuse and specular intensity rendertarget
	mRtDiffuse_SpecI = rdr->addRenderTarget(scrWid, scrHei, DXGI_FORMAT_R8G8B8A8_UNORM, mMSAASamples);
	// specular power and ??? rendertarget
	mRtSpecP = rdr->addRenderTarget(scrWid, scrHei, DXGI_FORMAT_R16G16_SNORM, mMSAASamples);

	mRtLighting = rdr->addRenderTarget(scrWid, scrHei, DXGI_FORMAT_R8G8B8A8_UNORM, mMSAASamples);

	mRtTmp = rdr->addRenderTarget(scrWid, scrHei, DXGI_FORMAT_R8G8B8A8_UNORM);
}

void DeferredPipeline::render(const RenderingData & data, const RenderingSettings & settings)
{
	//XMVECTOR tmp;
	//XMStoreFloat4x4(&data.ViewMatrix, XMMatrixInverse(&tmp, XMLoadFloat4x4(&data.SpotLights.Elements[0].WorldTransform)));
	

	if(settings.ShowBoundingSpheres) showBoundingSpheres(data);
	else
	{
		geometryStage(data, settings);
		lightingStage(data, settings);
	}
	if(settings.ShowGBuffer) showGBuffer(data);
}



// draws all meshes into G-buffer
void DeferredPipeline::geometryStage(const RenderingData & data, const RenderingSettings & settings)
{
	XMMATRIX view, proj, viewProj, worldView;
	view = XMLoadFloat4x4(&data.ViewMatrix);
	proj = XMLoadFloat4x4(&data.ProjMatrix);
	viewProj = XMMatrixMultiply(view, proj);

	Renderer * rdr = Renderer::Instance;

	TextureID RTs[] = { mRtDepth, mRtNormal, mRtDiffuse_SpecI, mRtSpecP };
	rdr->setRenderTargets(4, RTs, rdr->getDefaultDepthStencilBuffer());

	float color[] = { 0.0f, 0.0f, 0.0f, 0.0f };
	rdr->clear(true, color);

	rdr->setVertexFormat(VertexPositionNormalTangentTexture::VertexFormat);
	rdr->setPrimitiveType(TRIANGLE_LIST);

	rdr->setShader(mShaderGeometryStage);

	rdr->setShaderMatrix("gProj", (float*)&proj);

	uint meshesSize = data.Meshes.NumElements;
	for(uint meshIdx = 0; meshIdx < meshesSize; meshIdx++)
	{
		MeshInstance & meshInst = data.Meshes.Elements[meshIdx];
		Mesh & mesh = *meshInst.Mesh_;
		Material & material = *meshInst.Material_;

		worldView = XMMatrixMultiply(XMLoadFloat4x4(&meshInst.WorldTransform), view);
		rdr->setShaderMatrix("gWorldView", (float*)&worldView);
		rdr->setShaderFloatVector("gDiffColor", (float*)&material.DiffuseColor);
		rdr->setShaderFloatVector("gSpecIntensAndPower", (float*)&material.SpecularIntensityAndPower);
		rdr->setShaderResource("gDiffuseMap", material.DiffuseMap);
		rdr->setShaderResource("gNormalMap", material.NormalMap);
		rdr->setShaderResource("gSpecularMap", material.SpecularMap);

		uint partsSize = mesh.GetParts().size();
		for (uint partIdx = 0; partIdx < partsSize; partIdx++)
		{
			const Mesh::Part & part = mesh.GetParts()[partIdx];

			mmath::BoundingSphere meshBSphere = mmath::BoundingSphere::transform(part.BSphere,
				XMLoadFloat3(&meshInst.Position), XMLoadFloat4(&meshInst.Orientation), XMLoadFloat3(&meshInst.Scale));
			if(!mmath::frustumSphereintersects(data.ViewFrustum.Planes, meshBSphere))
				continue;

			uint stride = sizeof(VertexPositionNormalTangentTexture);
			uint offset = 0;

			rdr->setVertexBuffer(part.VB);
			rdr->setIndexBuffer(part.IB);

			rdr->drawIndexed();
		}
	}

	bricksGeometryStage(data);
}

void DeferredPipeline::bricksGeometryStage( const RenderingData & data )
{
	XMMATRIX view, worldView;
	view = XMLoadFloat4x4(&data.ViewMatrix);

	Renderer * rdr = Renderer::Instance;

	uint bricksCount = data.Bricks.size();
	for(uint brickIdx = 0; brickIdx < bricksCount; brickIdx++)
	{
		const BrickGfxData & brick = data.Bricks[brickIdx];
		Mesh & mesh = *data.BrickMesh;

		worldView = XMMatrixMultiply(XMLoadFloat4x4(&brick.WorldMatrix), view);
		rdr->setShaderMatrix("gWorldView", (float*)&worldView);
		rdr->setShaderFloatVector("gDiffColor", (float*)&brick.Color);

		XMFLOAT2 specIntensAndPower(0.5f, 128.0f);
		rdr->setShaderFloatVector("gSpecIntensAndPower", (float*)&specIntensAndPower);

		uint partsSize = mesh.GetParts().size();
		for (uint partIdx = 0; partIdx < partsSize; partIdx++)
		{
			const Mesh::Part & part = mesh.GetParts()[partIdx];

			mmath::BoundingSphere meshBSphere = mmath::BoundingSphere(brick.Position, brick.BSphereRadius);
			if(!mmath::frustumSphereintersects(data.ViewFrustum.Planes, meshBSphere))
				continue;

			uint stride = sizeof(VertexPositionNormalTangentTexture);
			uint offset = 0;

			rdr->setVertexBuffer(part.VB);
			rdr->setIndexBuffer(part.IB);

			rdr->drawIndexed();
		}
	}
}

void DeferredPipeline::lightingStage(const RenderingData & data, const RenderingSettings & settings)
{
	Renderer * rdr = Renderer::Instance;
	rdr->setPrimitiveType(TRIANGLE_LIST);
	rdr->setVertexFormat(VertexPosition::VertexFormat);
	mCSM->render(data);
	//renderDirectionalShadowMapInFrustum(data, data.ViewFrustum, mCascades[0]);
	renderSpotShadowMaps(data);

	if(settings.RenderSSAO)
		mSSAO->render(data.ProjMatrix, mRtDepth, mRtNormal, mRtSpecP);

	// clear light accumulation buffer
	rdr->setRenderTargets(1, &mRtLighting, rdr->getDefaultDepthStencilBuffer());
	float color[] = {0,0,0,0};
	rdr->clear(true, color, false, 0, false);

	// accumulate point and spot lighting
	renderPointLights(data);
	renderSpotLights(data);

	// accumulate directional lighting
	rdr->setRenderTargets(1, &mRtLighting, Renderer::TEXTURE_NONE);
	renderDirectionalLightsCSM(data, settings);

	//resolve msaa buffer to non-msaa or just use original lighting buffer if msaa not enabled
	TextureID resolvedLightingBuffer;
	if(mMSAAEnabled)
	{
		rdr->resolveMSAATexture(mRtLighting, mRtTmp);
		resolvedLightingBuffer = mRtTmp;
	}
	else
	{
		resolvedLightingBuffer = mRtLighting;
	}

	// copy light accumulation buffer into framebuffer, possibly adding screen space effects like SSAO
	TextureID framebuffer = rdr->getDefaultFramebuffer();
	rdr->setRenderTargets(1, &framebuffer, Renderer::TEXTURE_NONE);
	rdr->setShader(mShaderDeferredFinal);
	rdr->setShaderResource("gLightingBuffer", resolvedLightingBuffer);
	if(settings.RenderSSAO)
	{
		rdr->setShaderResource("gSSAOMap", mSSAO->FinalRenderTarget);
		rdr->setShaderFloat("gSSAO", 1.0f);
	}
	else
		rdr->setShaderFloat("gSSAO", 0.0f);
	rdr->drawFullScreenQuad();

	// detach light accum buffer from shader resource
	rdr->setShaderResource("gLightingBuffer", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gSSAOMap", Renderer::TEXTURE_NONE);
	rdr->applyConstantsWithoutDrawing();

	//renderLowFreq(data, settings, framebuffer, rdr->getDefaultDepthStencilBuffer(), *mCSM);

	//renderParticlesForward(data, settings, framebuffer, rdr->getDefaultDepthStencilBuffer(), *mCSM);
}

void DeferredPipeline::renderDirectionalLightsCSM(const RenderingData & data, const RenderingSettings & settings)
{
	XMMATRIX view, proj, invProj, invView;
	view = XMLoadFloat4x4(&data.ViewMatrix);
	proj = XMLoadFloat4x4(&data.ProjMatrix);
	XMVECTOR tmp;
	invProj = XMMatrixInverse(&tmp, proj);
	invView = XMMatrixInverse(&tmp, view);

	uint stride = sizeof(VertexPosition);
	uint offset = 0;

	// Prepare matrices that will transform view space pixel positions back to world space and then to light cascade projection space
	XMMATRIX cascadeMatrices[CSM::NUM_CASCADES];
	for (int i = 0; i < CSM::NUM_CASCADES ; i++)
	{
		cascadeMatrices[i] = XMMatrixMultiply(invView, XMLoadFloat4x4(&mCSM->Matrices[i]));
	}

	Renderer * rdr = Renderer::Instance;
	rdr->setShader(mShaderDirLight);

	rdr->setShaderMatrixArray("gInvView_LightViewProjs", (float*)cascadeMatrices, CSM::NUM_CASCADES);
	rdr->setShaderResource("gBigShadowMap", mCSM->AllCascadesMap);

	// Only far planes of cascades are needed, and SplitDepths holds both near and far cascade planes - 
	// - but far and near depth for neighbouring cascades are obviously the same,
	// so SplitDepths holds CASCADE_NUM + 1 values, and first one is unnecessary, since it holds near depth of first cascade
	// Another thing: below line will not work, if more than four values should be sent.
	rdr->setShaderFloatVector("gCascadeDepths", &mCSM->SplitDepths[1]);

	rdr->setShaderMatrix("gProjInv", (float*)&invProj);
	rdr->setShaderResource("gDepthTex", mRtDepth);
	rdr->setShaderResource("gNormal_SpecP_Tex", mRtNormal);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", mRtDiffuse_SpecI);
	rdr->setShaderResource("gSpecP_Tex",mRtSpecP);

	const DirectionalLight & light = data.MainLight;
	XMVECTOR lightDirVS = XMVector3Normalize(XMVector3TransformNormal(XMLoadFloat3(&light.Direction), view));
	XMFLOAT4 lightColorAndIntens = XMFLOAT4(light.Color.x, light.Color.y, light.Color.z, light.Intensity);
	rdr->setShaderFloatVector("gLightDirV", (float*)&lightDirVS);
	rdr->setShaderFloatVector("gLightColorAndIntens", (float*)&lightColorAndIntens);

	rdr->drawFullScreenQuad();

	// so there are no warnings about using rtv bound to shader as a srv (I don't know why there are warnings only for this rt)
	rdr->setShaderResource("gDepthTex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gSpecP_Tex", Renderer::TEXTURE_NONE);

	//ID3D11ShaderResourceView* tmpSRVArray[] = { NULL, NULL, NULL, NULL };
	//effect->GetVariableByName("gShadowMaps")->AsShaderResource()->SetResourceArray(tmpSRVArray, 0, NUM_CASCADES);
	rdr->setShaderResource("gBigShadowMap", Renderer::TEXTURE_NONE);

	rdr->applyConstantsWithoutDrawing();
}


void DeferredPipeline::renderDirectionalLights(const RenderingData & data)
{
	// INCORRECT WITH SHADER VARS BELOW!!!

	//XMMATRIX view, proj, invProj, invView;
	//view = XMLoadFloat4x4(&data.ViewMatrix);
	//proj = XMLoadFloat4x4(&data.ProjMatrix);
	//XMVECTOR tmp;
	//invProj = XMMatrixInverse(&tmp, proj);
	//invView = XMMatrixInverse(&tmp, view);

	//uint stride = sizeof(VertexPosition);
	//uint offset = 0;

	//mContext->IASetVertexBuffers(0, 1, &mFullScreenQuadVertexBuffer, &stride, &offset);
	//mContext->IASetIndexBuffer(mFullScreenQuadIndexBuffer, DXGI_FORMAT_R16_UINT, 0);

	//PS::DirLight->gProjInv->SetMatrix((float*)&invProj);
	//PS::DirLight->gDepthTex->SetResource(mRenderTargets[RT_DEPTH].SRView);
	//PS::DirLight->gNormal_SpecP_Tex->SetResource(mRenderTargets[RT_NORMAL].SRView);
	//PS::DirLight->gDiffuse_SpecI_Tex->SetResource(mRenderTargets[RT_DIFFUSE_SPECI].SRView);
	//PS::DirLight->gSpecP_Tex->SetResource(mRenderTargets[RT_SPECP].SRView);

	//const DirectionalLight & light = data.MainLight;
	//XMVECTOR lightDirVS = XMVector3Normalize(XMVector3TransformNormal(XMLoadFloat3(&light.Direction), view));
	//XMFLOAT4 lightColorAndIntens = XMFLOAT4(light.Color.x, light.Color.y, light.Color.z, light.Intensity);
	//PS::DirLight->gLightDirV->SetFloatVector((float*)&lightDirVS);
	//PS::DirLight->gLightColorAndIntens->SetFloatVector((float*)&lightColorAndIntens);

	//PS::DirLight->Pass->Apply(0, mContext);
	//mContext->DrawIndexed(6, 0, 0);

	//// so there are no warnings about using rtv bound to shader as a srv (I don't know why there are warnings only for this rt)
	//PS::DirLight->gDepthTex->SetResource(NULL);
	//PS::DirLight->gNormal_SpecP_Tex->SetResource(NULL);
	//PS::DirLight->gDiffuse_SpecI_Tex->SetResource(NULL);
	//PS::DirLight->gSpecP_Tex->SetResource(NULL);

	//PS::DirLight->Pass->Apply(0, mContext);
}

void DeferredPipeline::renderPointLights(const RenderingData & data)
{
	XMMATRIX view, proj, worldView;
	view = XMLoadFloat4x4(&data.ViewMatrix);
	proj = XMLoadFloat4x4(&data.ProjMatrix);

	Renderer * rdr = Renderer::Instance;

	const Mesh::Part & meshPart = mSphereMesh->GetParts()[0];

	rdr->setVertexBuffer(meshPart.VB);
	rdr->setIndexBuffer(meshPart.IB);
	rdr->setVertexFormat(VertexPosition::VertexFormat);
	rdr->setShader(mShaderPointLight);

	rdr->setShaderMatrix("gProj", (float*)&proj);
	rdr->setShaderResource("gDepthTex", mRtDepth);
	rdr->setShaderResource("gNormal_SpecP_Tex", mRtNormal);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", mRtDiffuse_SpecI);
	rdr->setShaderResource("gSpecP_Tex", mRtSpecP);

	for(uint i = 0; i < data.PointLights.NumElements; i++)
	{
		PointLight & light = data.PointLights.Elements[i];

		//XMVECTOR lightPos = XMVectorSet(light.WorldTransform._41, light.WorldTransform._42, light.WorldTransform._43, 1.0f);
		XMVECTOR lightPos = XMLoadFloat3(&light.Position);

		XMMATRIX world = XMMatrixMultiply(
			XMMatrixScaling(light.Radius, light.Radius, light.Radius),
			XMMatrixTranslationFromVector(lightPos)
			);

		//if(!checkMeshPartFrustumIntersection(*mSphereMesh, 0, world, data.ViewFrustum.Planes))
		//	continue;
		mmath::BoundingSphere meshBSphere = mmath::BoundingSphere::transform(meshPart.BSphere, lightPos, XMLoadFloat4(&light.Orientation), light.Radius);
		if(!mmath::frustumSphereintersects(data.ViewFrustum.Planes, meshBSphere))
			continue;

		worldView = XMMatrixMultiply(world, view);

		XMVECTOR lightPosVS = XMVector3Transform(lightPos, view);
		XMFLOAT4 lightColorAndIntens = XMFLOAT4(light.Color.x, light.Color.y, light.Color.z, light.Intensity);
		XMFLOAT2 lightRadiusAndFalloff = XMFLOAT2(light.Radius, light.Falloff);

		rdr->setShaderMatrix("gWorldView", (float*)&worldView);
		rdr->setShaderFloatVector("gLightPosV", (float*)&lightPosVS);
		rdr->setShaderFloatVector("gLightColorAndIntens", (float*)&lightColorAndIntens);
		rdr->setShaderFloatVector("gLightRadiusAndFalloff", (float*)&lightRadiusAndFalloff);

		rdr->drawIndexed();
	}

	// so there are no warnings about using rtv bound to shader as a srv (I don't know why there are warnings only for this rt)
	rdr->setShaderResource("gDepthTex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", Renderer::TEXTURE_NONE);
	//setShaderResource("gSpecP_Tex", TEXTURE_NONE);
	rdr->applyConstantsWithoutDrawing();
}

void DeferredPipeline::renderSpotShadowMaps(const RenderingData & data)
{
	Renderer * rdr = Renderer::Instance;
	Viewport oldVp = rdr->getViewport();

	for(uint i = 0; i < data.SpotLights.NumElements; i++)
	{
		SpotLight & light = data.SpotLights.Elements[i];
		if(!light.IsCastingShadows)
			continue;

		rdr->setViewportFromTextureDim(light.ShadowMap);

		rdr->setRenderTargets(0, NULL, light.ShadowMap);
		rdr->clear(false, NULL, true, 1.0f, false);

		XMMATRIX lightViewProj;

		// View matrix of object (light in this case) is inverse of it's world matrix
		XMVECTOR tmp;
		lightViewProj = XMMatrixMultiply(
			XMMatrixInverse(&tmp, XMLoadFloat4x4(&light.WorldTransform)),
			XMLoadFloat4x4(&light.ProjectionMatrix)
			);

		mmath::FrustumPersp lightFrustum;
		light.getViewFrustum(lightFrustum);

		uint meshesSize = data.Meshes.NumElements;
		for(uint meshIdx = 0; meshIdx < meshesSize; meshIdx++)
		{
			MeshInstance & meshInst = data.Meshes.Elements[meshIdx];
			Mesh & mesh = *meshInst.Mesh_;

			XMMATRIX worldViewProj = XMMatrixMultiply(XMLoadFloat4x4(&meshInst.WorldTransform), lightViewProj);

			rdr->setShader(mShaderShadowMap);

			rdr->setShaderMatrix("gWVP", (float*)&worldViewProj);
			//mShadowMapEffect->Effect->GetVariableByName("gWVP")->AsMatrix()->SetMatrix((float*)&worldViewProj);

			uint partsSize = mesh.GetParts().size();
			for (uint partIdx = 0; partIdx < partsSize; partIdx++)
			{
				const Mesh::Part & part = mesh.GetParts()[partIdx];

				mmath::BoundingSphere meshBSphere = mmath::BoundingSphere::transform(part.BSphere,
					XMLoadFloat3(&meshInst.Position), XMLoadFloat4(&meshInst.Orientation), XMLoadFloat3(&meshInst.Scale));
				if(!mmath::frustumSphereintersects(lightFrustum.Planes, meshBSphere))
					continue;

				uint stride = sizeof(VertexPositionNormalTangentTexture);
				uint offset = 0;

				rdr->setVertexBuffer(part.VB);
				rdr->setIndexBuffer(part.IB);

				rdr->drawIndexed();
			}
		}
	}

	rdr->setViewport(oldVp);
}




void DeferredPipeline::renderSpotLights(const RenderingData & data)
{
	XMMATRIX view, proj, invView, worldView;
	view = XMLoadFloat4x4(&data.ViewMatrix);
	proj = XMLoadFloat4x4(&data.ProjMatrix);
	XMVECTOR tmp;
	invView = XMMatrixInverse(&tmp, view);

	const Mesh::Part & meshPart = mConeMesh->GetParts()[1];

	Renderer * rdr = Renderer::Instance;
	rdr->setVertexBuffer(meshPart.VB);
	rdr->setIndexBuffer(meshPart.IB);
	rdr->setVertexFormat(VertexPosition::VertexFormat);

	for(uint i = 0; i < data.SpotLights.NumElements; i++)
	{
		SpotLight & light = data.SpotLights.Elements[i];

		if(light.IsCastingShadows)
		{
			rdr->setShader(mShaderShadowedSpotLight);

			XMMATRIX lightViewProj, invView_LightViewProj;

			// View matrix of object (light in this case) is inverse of it's world matrix
			XMVECTOR tmp;
			lightViewProj = XMMatrixMultiply(
				XMMatrixInverse(&tmp, XMLoadFloat4x4(&light.WorldTransform)),
				XMLoadFloat4x4(&light.ProjectionMatrix)
				);

			invView_LightViewProj = XMMatrixMultiply(invView, lightViewProj);

			rdr->setShaderMatrix("gInvView_LightViewProj", (float*)&invView_LightViewProj);
			rdr->setShaderResource("gShadowMap", light.ShadowMap);
		}
		else
		{
			rdr->setShader(mShaderSpotLight);
		}

		rdr->setShaderMatrix("gProj", (float*)&proj);
		rdr->setShaderResource("gDepthTex", mRtDepth);
		rdr->setShaderResource("gNormal_SpecP_Tex", mRtNormal);
		rdr->setShaderResource("gDiffuse_SpecI_Tex", mRtDiffuse_SpecI);
		rdr->setShaderResource("gSpecP_Tex", mRtSpecP);

		// Spot light is drawn using cone geometry. It must be properly scaled.
		// Along cone's revolution axis, light radius value is used as a scale.
		// Along other two axes, scaling has to be calculated using light radius and angle.
		float coneCosAngle = light.SpotCosAngle;
		float xyConeScale = light.Radius * sqrtf(1.f - coneCosAngle * coneCosAngle) / coneCosAngle;
		// To create correct bounding sphere, maximum of two scales is used.
		float bSphereScale = (std::max)(xyConeScale, light.Radius);

		XMVECTOR lightPos = XMLoadFloat3(&light.Position);
		XMVECTOR lightOrient = XMLoadFloat4(&light.Orientation);

		XMMATRIX lightWorldNoScale = XMMatrixMultiply(
			XMMatrixRotationQuaternion(lightOrient),
			XMMatrixTranslationFromVector(lightPos)
		);

		mmath::BoundingSphere meshBSphere = mmath::BoundingSphere::transform(meshPart.BSphere, lightPos, lightOrient, bSphereScale);
		if(!mmath::frustumSphereintersects(data.ViewFrustum.Planes, meshBSphere))
			continue;

		// Assumes that cone's main axis in local space is Z-axis.
		XMMATRIX world = XMMatrixMultiply(
			XMMatrixScaling(xyConeScale, xyConeScale, light.Radius),
			lightWorldNoScale
			);

		worldView = XMMatrixMultiply(world, view);

		XMVECTOR lightPosVS = XMVector3Transform(lightPos, view);
		XMVECTOR lightDirVS = XMVector3Normalize(XMVector3TransformNormal(
			XMVector3TransformNormal(SpotLight::InitialDirection, lightWorldNoScale),
			view));
		XMFLOAT4 lightColorAndIntens = XMFLOAT4(light.Color.x, light.Color.y, light.Color.z, light.Intensity);
		XMFLOAT4 lightParams = XMFLOAT4(light.Radius, light.Falloff, light.SpotCosAngle, light.SpotFalloff);

		rdr->setShaderMatrix("gWorldView", (float*)&worldView);
		rdr->setShaderFloatVector("gLightPosV", (float*)&lightPosVS);
		rdr->setShaderFloatVector("gLightSpotDirV", (float*)&lightDirVS);
		rdr->setShaderFloatVector("gLightColorAndIntens", (float*)&lightColorAndIntens);
		rdr->setShaderFloatVector("gLightParams", (float*)&lightParams);

		rdr->drawIndexed();
	}

	rdr->setShader(mShaderSpotLight);
	rdr->setShaderResource("gDepthTex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gSpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->applyConstantsWithoutDrawing();

	rdr->setShader(mShaderShadowedSpotLight);
	rdr->setShaderResource("gDepthTex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gSpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->applyConstantsWithoutDrawing();
}

void DeferredPipeline::showBoundingSpheres(const RenderingData & data)
{
	XMMATRIX viewProj;
	viewProj = XMMatrixMultiply( XMLoadFloat4x4(&data.ViewMatrix), XMLoadFloat4x4(&data.ProjMatrix));

	for(uint i = 0; i < data.Meshes.NumElements; i++)
		debug::renderMeshBoundingSphere(data.Meshes[i], viewProj);

	for(uint i = 0; i < data.PointLights.NumElements; i++)
		debug::renderPointLightBoundingSphere(data.PointLights[i], viewProj);

	for(uint i = 0; i < data.SpotLights.NumElements; i++)
		debug::renderSpotLightBoundingSphere(data.SpotLights[i], viewProj);

	for(uint i = 0; i < data.SpotLights.NumElements; i++)
		debug::renderSpotLightBoundingCone(data.SpotLights[i], viewProj);
}

void DeferredPipeline::showGBuffer(const RenderingData & data)
{
	Renderer * rdr = Renderer::Instance;

	TextureID framebuffer = rdr->getDefaultFramebuffer();
	rdr->setRenderTargets(1, &framebuffer, rdr->getDefaultDepthStencilBuffer());

	Viewport oldVp = rdr->getViewport();

	Viewport vp;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;

	const int MODE_DEPTH = 0;
	const int MODE_NORMAL = 1;
	const int MODE_DIFFUSE = 2;
	const int MODE_SPEC_INTENS = 3;

	float scrWid = (float)mcore::GlobalAppData::Instance.ScreenWidth;
	float scrHei = (float)mcore::GlobalAppData::Instance.ScreenHeight;

	rdr->setShader(mShaderDrawGBuffer);

	rdr->setShaderResource("gDepthTex", mRtDepth);
	rdr->setShaderResource("gNormal_SpecP_Tex", mRtNormal);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", mRtDiffuse_SpecI);
	rdr->setShaderResource("gSpecP_Tex", mRtSpecP);

	//effect->GetVariableByName("gDepthTex")->AsShaderResource()->SetResource(mCascades[0].ShadowMap.SRView);
	//effect->GetVariableByName("gDepthTex")->AsShaderResource()->SetResource(mRenderTargets[RT_DEPTH].SRView);

	// depth

	vp.TopLeftX = scrWid - scrWid / 3.5f;
	vp.TopLeftY = scrHei / 10;
	vp.Width = scrWid / 3.5f;
	vp.Height = scrHei / 3.5f;
	rdr->setViewport(vp);

	float viewportRes[] = { scrWid, scrHei };
	rdr->setShaderFloatVector("gGBufferRes", viewportRes);

	rdr->setShaderInt("gMode", MODE_DEPTH);
	rdr->drawFullScreenQuad();

	// normals

	vp.TopLeftX = scrWid - scrWid / 3.5f;
	vp.TopLeftY = scrHei / 10 + scrHei / 3.5f;
	vp.Width = scrWid / 3.5f;
	vp.Height = scrHei / 3.5f;
	rdr->setViewport(vp);

	rdr->setShaderInt("gMode", MODE_NORMAL);
	rdr->drawFullScreenQuad();

	// diffuse

	vp.TopLeftX = scrWid - scrWid / 3.5f;
	vp.TopLeftY = scrHei / 10 + scrHei / 3.5f + scrHei / 3.5f;
	vp.Width = scrWid / 3.5f;
	vp.Height = scrHei / 3.5f;
	rdr->setViewport(vp);

	rdr->setShaderInt("gMode", MODE_DIFFUSE);
	rdr->drawFullScreenQuad();

	//// specular intensity

	//vp.Width = (float)light.ShadowMapWidth;
	//vp.Height = (float)light.ShadowMapHeight;
	//mContext->RSSetViewports(1, &vp);

	//effect->GetVariableByName("gMode")->AsScalar()->SetInt(MODE_SPEC_INTENS);
	//mDrawGBufferEffectPass->Apply(0, mContext);
	//mContext->DrawIndexed(6, 0, 0);
	// 

	rdr->setShaderResource("gDepthTex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gNormal_SpecP_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gDiffuse_SpecI_Tex", Renderer::TEXTURE_NONE);
	rdr->setShaderResource("gSpecP_Tex", Renderer::TEXTURE_NONE);

	rdr->applyConstantsWithoutDrawing();

	rdr->setViewport(oldVp);
}
